#include "modbus.h"
#include "clock.h"
#include "switch.h"
#include "stm8s.h"

type_modbus modbus_data = {DISABLE, 0};

void modbus_init(void)
{
  UART1_DeInit();
  UART1_Init(115200, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, UART1_PARITY_NO, UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);
  UART1_Cmd(ENABLE);
  UART1_ITConfig(UART1_IT_RXNE_OR, ENABLE); 
}

uint8_t* modbus_txpc(void)
{
  return modbus_data.data_tx;
}

uint16_t modbus_crc(uint8_t *data, uint8_t len)
{
  uint16_t crc = 0xffff;
  uint8_t i, j;
  for(i=0; i<len; ++i)
  {
    crc ^= *data++;
    for(j=0; j<8; ++j)
    {
      if(crc&0x0001)
      {
        crc >>= 1;
        crc ^= 0xa001;
      }
      else
      {
        crc >>= 1;
      }
    }
  }
  return crc;
}

void modbus_init_struct(void)
{
  modbus_data.pc_node = 0;
  modbus_data.pc_data = 0;
  modbus_data.timer = 0;
}

uint8_t modbus_write_data(uint8_t* data, uint8_t len)
{
  uint8_t i;
  while(len--)
  {
    for(i=0;i<MODBUS_TX_TIME&&!UART1_GetFlagStatus(UART1_FLAG_TXE);i++);
    if(i==MODBUS_TX_TIME)
    {
      return 1;
    }
    UART1_SendData8(*data++);
  }
  return 0;
}

void modbus_write(uint8_t *data, uint8_t len)
{
  type_s2c crc;
  *data = MODBUS_NODE;
  crc.s = modbus_crc(data, len);
  data[len] = crc.c[1];
  data[len+1] = crc.c[0];
  assert_param(modbus_write_data(data, len+2)==0);
}

void modbus_err(uint8_t code, uint8_t err)
{
  modbus_data.data_tx[0] = MODBUS_NODE;
  modbus_data.data_tx[1] = code|0x80;
  modbus_data.data_tx[2] = err;
  modbus_write(modbus_data.data_tx, 3);
}

void modbus_tree(uint8_t *pc)
{
  if(*(pc+1)==8)
  {
    modbus_write_data(pc, 8);
    return;
  }
  switch(*(pc+2)&0xf0)
  {
  //case 0x00: info_modbus(pc+1); break;
  case 0x10: clock_modbus(pc+1);        break;
  case 0x20: switch_modbus(pc+1);       break;
  default: modbus_err(*(pc+1), 2);
  }
}

void modbus_check(void)
{
  uint8_t *pc;
  type_s2c crc;
  for(uint8_t i=0;i<modbus_data.pc_node;++i)
  {
    if(modbus_data.node[i]+8 == modbus_data.pc_data || modbus_data.node[i]+9+modbus_data.data[modbus_data.node[i]+6] == modbus_data.pc_data)
    {
      pc = &modbus_data.data[modbus_data.node[i]];
      crc.s = modbus_crc(pc, modbus_data.pc_data-modbus_data.node[i]-2);
      if(crc.c[1] == modbus_data.data[modbus_data.pc_data-2] && crc.c[0] == modbus_data.data[modbus_data.pc_data-1])
      {
        modbus_tree(pc);
        modbus_init_struct();
        break;
      }
    }
  }
  modbus_data.flag = DISABLE;
}

void modbus_fun(void)
{
  if(modbus_data.flag==DISABLE)return;
  modbus_check();
}

void modbus_interrupt(void)
{
  if(!UART1_GetFlagStatus(UART1_FLAG_RXNE))
  {
    UART1_ClearFlag(UART1_FLAG_LBDF);
    return;
  }
  modbus_data.timer = 0;
  modbus_data.data[modbus_data.pc_data&0x1f] = UART1_ReceiveData8();
  UART1_ClearITPendingBit(UART1_IT_RXNE);
  if(modbus_data.data[modbus_data.pc_data++] == MODBUS_NODE)
  {
    modbus_data.node[modbus_data.pc_node++] = modbus_data.pc_data-1;
  }
  modbus_data.flag = ENABLE;
}

void modbus_timer(void)
{
  if(++modbus_data.timer==10)
  {
    modbus_data.timer = 0;
    modbus_init_struct();
  }
}